﻿using System;
using System.Collections;
using System.Collections.Generic;

namespace Mbl.ParameterizedTest
{
    public class TestCollection<TIn1, TOut> : IEnumerable<TestCase<TIn1, TOut>>
    {
        private readonly Action _setup;
        private readonly Func<TIn1, TOut> _methodUnderTest;
        private readonly List<TestCase<TIn1, TOut>> _testCases = new List<TestCase<TIn1, TOut>>();

        public TestCollection(Action setup, Func<TIn1, TOut> methodUnderTest)
        {
            _setup = setup;
            _methodUnderTest = methodUnderTest;
        }

        public IEnumerator<TestCase<TIn1, TOut>> GetEnumerator()
        {
            return _testCases.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        public void AddTestCase(TestCase<TIn1, TOut> testCase)
        {
			if (testCase == null)
			{
				throw new ArgumentNullException(nameof(testCase));
			}

            _testCases.Add(testCase);            
        }

        public void Evaluate()
        {
            var aggregator = new TestResultAggregator(new DefaultTestFailer());            
            foreach (var testCase in _testCases)
            {
				var testResult = EvaluateTestCase(testCase);                
                aggregator.AddResult(testResult);                
            }

            aggregator.FailIfAnyErrors();
        }

		private TestCaseAssertionResult EvaluateTestCase(TestCase<TIn1, TOut> testCase)
		{
			try {
				_setup();                 
			}
			catch(Exception e)
			{
				return TestCaseAssertionResult.Error(testCase, $"An unexpected exception was thrown during test setup: {e}");
			}
                
            Func<TOut> actual = () => _methodUnderTest(testCase.In1);

			try { 
				return testCase.HoldsFor(actual);
			}
			catch(Exception e)
			{
				return TestCaseAssertionResult.Error(testCase, $"An unexpected exception was while evaluating the test: {e}");
			}
		}
    }

    public class TestCollection<TIn1, TIn2, TOut> : IEnumerable<TestCase<TIn1, TIn2, TOut>>
    {
        private readonly Action _setup;
        private readonly Func<TIn1, TIn2, TOut> _methodUnderTest;
        private readonly List<TestCase<TIn1, TIn2, TOut>> _testCases = new List<TestCase<TIn1, TIn2, TOut>>();

        public TestCollection(Action setup, Func<TIn1, TIn2, TOut> methodUnderTest)
        {
            _setup = setup;
            _methodUnderTest = methodUnderTest;
        }

        public IEnumerator<TestCase<TIn1, TIn2, TOut>> GetEnumerator()
        {
            return _testCases.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        public void AddTestCase(TestCase<TIn1, TIn2, TOut> testCase)
        {
			if (testCase == null)
			{
				throw new ArgumentNullException(nameof(testCase));
			}

            _testCases.Add(testCase);            
        }

        public void Evaluate()
        {
            var aggregator = new TestResultAggregator(new DefaultTestFailer());            
            foreach (var testCase in _testCases)
            {
				var testResult = EvaluateTestCase(testCase);                
                aggregator.AddResult(testResult);                
            }

            aggregator.FailIfAnyErrors();
        }

		private TestCaseAssertionResult EvaluateTestCase(TestCase<TIn1, TIn2, TOut> testCase)
		{
			try {
				_setup();                 
			}
			catch(Exception e)
			{
				return TestCaseAssertionResult.Error(testCase, $"An unexpected exception was thrown during test setup: {e}");
			}
                
            Func<TOut> actual = () => _methodUnderTest(testCase.In1, testCase.In2);

			try { 
				return testCase.HoldsFor(actual);
			}
			catch(Exception e)
			{
				return TestCaseAssertionResult.Error(testCase, $"An unexpected exception was while evaluating the test: {e}");
			}
		}
    }

    public class TestCollection<TIn1, TIn2, TIn3, TOut> : IEnumerable<TestCase<TIn1, TIn2, TIn3, TOut>>
    {
        private readonly Action _setup;
        private readonly Func<TIn1, TIn2, TIn3, TOut> _methodUnderTest;
        private readonly List<TestCase<TIn1, TIn2, TIn3, TOut>> _testCases = new List<TestCase<TIn1, TIn2, TIn3, TOut>>();

        public TestCollection(Action setup, Func<TIn1, TIn2, TIn3, TOut> methodUnderTest)
        {
            _setup = setup;
            _methodUnderTest = methodUnderTest;
        }

        public IEnumerator<TestCase<TIn1, TIn2, TIn3, TOut>> GetEnumerator()
        {
            return _testCases.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        public void AddTestCase(TestCase<TIn1, TIn2, TIn3, TOut> testCase)
        {
			if (testCase == null)
			{
				throw new ArgumentNullException(nameof(testCase));
			}

            _testCases.Add(testCase);            
        }

        public void Evaluate()
        {
            var aggregator = new TestResultAggregator(new DefaultTestFailer());            
            foreach (var testCase in _testCases)
            {
				var testResult = EvaluateTestCase(testCase);                
                aggregator.AddResult(testResult);                
            }

            aggregator.FailIfAnyErrors();
        }

		private TestCaseAssertionResult EvaluateTestCase(TestCase<TIn1, TIn2, TIn3, TOut> testCase)
		{
			try {
				_setup();                 
			}
			catch(Exception e)
			{
				return TestCaseAssertionResult.Error(testCase, $"An unexpected exception was thrown during test setup: {e}");
			}
                
            Func<TOut> actual = () => _methodUnderTest(testCase.In1, testCase.In2, testCase.In3);

			try { 
				return testCase.HoldsFor(actual);
			}
			catch(Exception e)
			{
				return TestCaseAssertionResult.Error(testCase, $"An unexpected exception was while evaluating the test: {e}");
			}
		}
    }

    public class TestCollection<TIn1, TIn2, TIn3, TIn4, TOut> : IEnumerable<TestCase<TIn1, TIn2, TIn3, TIn4, TOut>>
    {
        private readonly Action _setup;
        private readonly Func<TIn1, TIn2, TIn3, TIn4, TOut> _methodUnderTest;
        private readonly List<TestCase<TIn1, TIn2, TIn3, TIn4, TOut>> _testCases = new List<TestCase<TIn1, TIn2, TIn3, TIn4, TOut>>();

        public TestCollection(Action setup, Func<TIn1, TIn2, TIn3, TIn4, TOut> methodUnderTest)
        {
            _setup = setup;
            _methodUnderTest = methodUnderTest;
        }

        public IEnumerator<TestCase<TIn1, TIn2, TIn3, TIn4, TOut>> GetEnumerator()
        {
            return _testCases.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        public void AddTestCase(TestCase<TIn1, TIn2, TIn3, TIn4, TOut> testCase)
        {
			if (testCase == null)
			{
				throw new ArgumentNullException(nameof(testCase));
			}

            _testCases.Add(testCase);            
        }

        public void Evaluate()
        {
            var aggregator = new TestResultAggregator(new DefaultTestFailer());            
            foreach (var testCase in _testCases)
            {
				var testResult = EvaluateTestCase(testCase);                
                aggregator.AddResult(testResult);                
            }

            aggregator.FailIfAnyErrors();
        }

		private TestCaseAssertionResult EvaluateTestCase(TestCase<TIn1, TIn2, TIn3, TIn4, TOut> testCase)
		{
			try {
				_setup();                 
			}
			catch(Exception e)
			{
				return TestCaseAssertionResult.Error(testCase, $"An unexpected exception was thrown during test setup: {e}");
			}
                
            Func<TOut> actual = () => _methodUnderTest(testCase.In1, testCase.In2, testCase.In3, testCase.In4);

			try { 
				return testCase.HoldsFor(actual);
			}
			catch(Exception e)
			{
				return TestCaseAssertionResult.Error(testCase, $"An unexpected exception was while evaluating the test: {e}");
			}
		}
    }

    public class TestCollection<TIn1, TIn2, TIn3, TIn4, TIn5, TOut> : IEnumerable<TestCase<TIn1, TIn2, TIn3, TIn4, TIn5, TOut>>
    {
        private readonly Action _setup;
        private readonly Func<TIn1, TIn2, TIn3, TIn4, TIn5, TOut> _methodUnderTest;
        private readonly List<TestCase<TIn1, TIn2, TIn3, TIn4, TIn5, TOut>> _testCases = new List<TestCase<TIn1, TIn2, TIn3, TIn4, TIn5, TOut>>();

        public TestCollection(Action setup, Func<TIn1, TIn2, TIn3, TIn4, TIn5, TOut> methodUnderTest)
        {
            _setup = setup;
            _methodUnderTest = methodUnderTest;
        }

        public IEnumerator<TestCase<TIn1, TIn2, TIn3, TIn4, TIn5, TOut>> GetEnumerator()
        {
            return _testCases.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        public void AddTestCase(TestCase<TIn1, TIn2, TIn3, TIn4, TIn5, TOut> testCase)
        {
			if (testCase == null)
			{
				throw new ArgumentNullException(nameof(testCase));
			}

            _testCases.Add(testCase);            
        }

        public void Evaluate()
        {
            var aggregator = new TestResultAggregator(new DefaultTestFailer());            
            foreach (var testCase in _testCases)
            {
				var testResult = EvaluateTestCase(testCase);                
                aggregator.AddResult(testResult);                
            }

            aggregator.FailIfAnyErrors();
        }

		private TestCaseAssertionResult EvaluateTestCase(TestCase<TIn1, TIn2, TIn3, TIn4, TIn5, TOut> testCase)
		{
			try {
				_setup();                 
			}
			catch(Exception e)
			{
				return TestCaseAssertionResult.Error(testCase, $"An unexpected exception was thrown during test setup: {e}");
			}
                
            Func<TOut> actual = () => _methodUnderTest(testCase.In1, testCase.In2, testCase.In3, testCase.In4, testCase.In5);

			try { 
				return testCase.HoldsFor(actual);
			}
			catch(Exception e)
			{
				return TestCaseAssertionResult.Error(testCase, $"An unexpected exception was while evaluating the test: {e}");
			}
		}
    }

    public class TestCollection<TIn1, TIn2, TIn3, TIn4, TIn5, TIn6, TOut> : IEnumerable<TestCase<TIn1, TIn2, TIn3, TIn4, TIn5, TIn6, TOut>>
    {
        private readonly Action _setup;
        private readonly Func<TIn1, TIn2, TIn3, TIn4, TIn5, TIn6, TOut> _methodUnderTest;
        private readonly List<TestCase<TIn1, TIn2, TIn3, TIn4, TIn5, TIn6, TOut>> _testCases = new List<TestCase<TIn1, TIn2, TIn3, TIn4, TIn5, TIn6, TOut>>();

        public TestCollection(Action setup, Func<TIn1, TIn2, TIn3, TIn4, TIn5, TIn6, TOut> methodUnderTest)
        {
            _setup = setup;
            _methodUnderTest = methodUnderTest;
        }

        public IEnumerator<TestCase<TIn1, TIn2, TIn3, TIn4, TIn5, TIn6, TOut>> GetEnumerator()
        {
            return _testCases.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        public void AddTestCase(TestCase<TIn1, TIn2, TIn3, TIn4, TIn5, TIn6, TOut> testCase)
        {
			if (testCase == null)
			{
				throw new ArgumentNullException(nameof(testCase));
			}

            _testCases.Add(testCase);            
        }

        public void Evaluate()
        {
            var aggregator = new TestResultAggregator(new DefaultTestFailer());            
            foreach (var testCase in _testCases)
            {
				var testResult = EvaluateTestCase(testCase);                
                aggregator.AddResult(testResult);                
            }

            aggregator.FailIfAnyErrors();
        }

		private TestCaseAssertionResult EvaluateTestCase(TestCase<TIn1, TIn2, TIn3, TIn4, TIn5, TIn6, TOut> testCase)
		{
			try {
				_setup();                 
			}
			catch(Exception e)
			{
				return TestCaseAssertionResult.Error(testCase, $"An unexpected exception was thrown during test setup: {e}");
			}
                
            Func<TOut> actual = () => _methodUnderTest(testCase.In1, testCase.In2, testCase.In3, testCase.In4, testCase.In5, testCase.In6);

			try { 
				return testCase.HoldsFor(actual);
			}
			catch(Exception e)
			{
				return TestCaseAssertionResult.Error(testCase, $"An unexpected exception was while evaluating the test: {e}");
			}
		}
    }

}
